"""视频帧处理器模块"""

import cv2
import numpy as np
import logging
from typing import Dict, List, Tuple, Optional, Any
from threading import Lock
from datetime import datetime

# 尝试导入配置模块，如果失败则使用默认配置
config_module_available = True
try:
    from config import get_config
except ImportError:
    config_module_available = False
    logging.warning("配置模块导入失败，将使用默认值")

def get_config(key, default=None):
    """获取配置项，如果配置模块不可用则返回默认值"""
    if config_module_available:
        try:
            from config import get_config as original_get_config
            return original_get_config(key, default)
        except Exception:
            pass
    return default

# 设置日志记录器
logger = logging.getLogger(__name__)

class FrameProcessor:
    """视频帧处理器，负责视频帧的预处理和后处理"""
    
    def __init__(self):
        """初始化帧处理器"""
        logger.info("开始初始化帧处理器...")
        self.lock = Lock()  # 添加线程锁确保线程安全
        self.camera_settings = {}  # 添加摄像头设置字典
        
        try:
            # 预处理参数
            self.resize_width = get_config('frame_processing.resize.width', 0)
            self.resize_height = get_config('frame_processing.resize.height', 0)
            self.maintain_aspect_ratio = get_config('frame_processing.resize.maintain_aspect_ratio', True)
            self.letterboxing = get_config('frame_processing.resize.letterboxing', False)
            
            # 图像增强参数
            self.brightness = get_config('frame_processing.enhancement.brightness', 0.0)
            self.contrast = get_config('frame_processing.enhancement.contrast', 1.0)
            self.saturation = get_config('frame_processing.enhancement.saturation', 1.0)
            self.sharpness = get_config('frame_processing.enhancement.sharpness', 0.0)
            
            # 后处理参数
            self.show_fps = get_config('frame_processing.display.show_fps', True)
            self.show_timestamp = get_config('frame_processing.display.show_timestamp', True)
            self.show_camera_info = get_config('frame_processing.display.show_camera_info', True)
            
            # 分析区域设置
            self.roi_points = None  # 感兴趣区域
            self.detection_zones = []  # 检测区域列表
            
            logger.info("帧处理器初始化成功")
            logger.debug(f"初始化参数: 调整大小({self.resize_width}x{self.resize_height}), "
                         f"亮度({self.brightness}), 对比度({self.contrast})")
        except Exception as e:
            logger.error(f"帧处理器初始化失败: {str(e)}", exc_info=True)
    
    def preprocess(self, frame: np.ndarray, camera_id: str = None) -> Tuple[np.ndarray, Dict]:
        """预处理视频帧
        
        Args:
            frame: 输入视频帧
            camera_id: 摄像头ID（可选）
            
        Returns:
            (预处理后的帧, 处理元数据)
        """
        # 如果有摄像头特定设置，使用它
        with self.lock:
            if camera_id and camera_id in self.camera_settings:
                # 可以在这里应用摄像头特定的预处理设置
                pass
        try:
            # 验证输入帧的有效性
            if frame is None or not isinstance(frame, np.ndarray):
                logger.error(f"无效的输入帧: {type(frame)}")
                return np.array([]), {}
            
            if frame.size == 0 or len(frame.shape) < 2:
                logger.error(f"空帧或格式无效: {frame.shape}")
                return np.array([]), {}
                
            logger.debug(f"开始预处理帧，摄像头ID: {camera_id}")
            metadata = {}
            processed_frame = frame.copy()
            
            # 调整大小
            if self.resize_width > 0 or self.resize_height > 0:
                original_shape = processed_frame.shape[:2]
                processed_frame, resize_info = self._resize_frame(
                    processed_frame,
                    self.resize_width,
                    self.resize_height,
                    self.maintain_aspect_ratio,
                    self.letterboxing
                )
                metadata['resize'] = resize_info
                metadata['original_shape'] = original_shape
                logger.debug(f"帧大小调整: {original_shape} -> {processed_frame.shape[:2]}")
            
            # 图像增强
            if any([self.brightness != 0.0, self.contrast != 1.0, 
                    self.saturation != 1.0, self.sharpness != 0.0]):
                processed_frame = self._enhance_image(
                    processed_frame,
                    self.brightness,
                    self.contrast,
                    self.saturation,
                    self.sharpness
                )
                logger.debug("应用图像增强")
            
            # 应用ROI
            if self.roi_points is not None:
                processed_frame, roi_mask = self._apply_roi(processed_frame, self.roi_points)
                metadata['roi_mask'] = roi_mask
                logger.debug("应用ROI掩码")
                
            logger.debug("帧预处理完成")
            return processed_frame, metadata
        except Exception as e:
            logger.error(f"帧预处理失败: {str(e)}", exc_info=True)
            # 返回空帧和空元数据以允许系统继续运行
            return np.array([]), {}
    
    def _resize_frame(self, frame: np.ndarray, width: int, height: int, 
                     maintain_ratio: bool, letterbox: bool) -> Tuple[np.ndarray, Dict]:
        """调整帧大小
        
        Args:
            frame: 输入帧
            width: 目标宽度
            height: 目标高度
            maintain_ratio: 是否保持宽高比
            letterbox: 是否使用letterboxing填充
            
        Returns:
            (调整大小后的帧, 调整信息)
        """
        try:
            # 验证输入帧
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame, {'error': '无效的输入帧'}
                
            original_height, original_width = frame.shape[:2]
            info = {
                'original_size': (original_width, original_height),
                'scaled_size': (original_width, original_height),
                'scale_factor': (1.0, 1.0),
                'pad': (0, 0, 0, 0)  # (top, bottom, left, right)
            }
            
            # 检查原始尺寸是否有效，防止除零错误
            if original_width <= 0 or original_height <= 0:
                logger.warning(f"无效的帧尺寸: {original_width}x{original_height}")
                return frame, info
                
            logger.debug(f"调整帧大小: {original_width}x{original_height} -> {width}x{height}, "
                        f"保持比例: {maintain_ratio}, Letterbox: {letterbox}")
                        
            if maintain_ratio:
                # 计算缩放比例，避免除零错误
                if width > 0 and height > 0:
                    scale = min(width / original_width, height / original_height)
                elif width > 0:
                    scale = width / original_width
                elif height > 0:
                    scale = height / original_height
                else:
                    scale = 1.0  # 如果没有指定目标尺寸，不缩放
                
                new_width = int(original_width * scale)
                new_height = int(original_height * scale)
                
                # 确保新尺寸有效
                new_width = max(1, new_width)
                new_height = max(1, new_height)
                
                info['scaled_size'] = (new_width, new_height)
                info['scale_factor'] = (scale, scale)
                
                # 调整大小
                resized = cv2.resize(frame, (new_width, new_height))
                
                if letterbox and (new_width != width or new_height != height) and width > 0 and height > 0:
                    # 计算填充大小
                    top = (height - new_height) // 2
                    bottom = height - new_height - top
                    left = (width - new_width) // 2
                    right = width - new_width - left
                    
                    # 应用填充
                    resized = cv2.copyMakeBorder(
                        resized,
                        top, bottom, left, right,
                        cv2.BORDER_CONSTANT,
                        value=[0, 0, 0]
                    )
                    info['pad'] = (top, bottom, left, right)
                    logger.debug(f"应用Letterbox填充: {top}, {bottom}, {left}, {right}")
                    
                logger.debug(f"保持比例调整完成: {original_width}x{original_height} -> {new_width}x{new_height}")
                return resized, info
            else:
                # 直接调整到目标大小
                target_width = width if width > 0 else original_width
                target_height = height if height > 0 else original_height
                
                # 确保目标尺寸有效
                target_width = max(1, target_width)
                target_height = max(1, target_height)
                
                resized = cv2.resize(frame, (target_width, target_height))
                info['scaled_size'] = (target_width, target_height)
                
                # 避免除零错误
                scale_x = target_width / original_width if original_width > 0 else 1.0
                scale_y = target_height / original_height if original_height > 0 else 1.0
                info['scale_factor'] = (scale_x, scale_y)
                
                logger.debug(f"直接调整完成: {original_width}x{original_height} -> {target_width}x{target_height}")
                return resized, info
        except Exception as e:
            logger.error(f"调整帧大小失败: {str(e)}", exc_info=True)
            # 返回原始帧和错误信息
            return frame, {'error': str(e)}

    
    def _enhance_image(self, frame: np.ndarray, brightness: float, contrast: float,
                       saturation: float, sharpness: float) -> np.ndarray:
        """增强图像质量
        
        Args:
            frame: 输入帧
            brightness: 亮度调整 (-1.0 到 1.0)
            contrast: 对比度调整 (0.0 及以上)
            saturation: 饱和度调整 (0.0 及以上)
            sharpness: 锐度调整 (0.0 及以上)
            
        Returns:
            增强后的图像
        """
        try:
            # 验证输入帧
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame
                
            # 验证参数范围
            brightness = np.clip(brightness, -1.0, 1.0)
            contrast = max(0.0, contrast)
            saturation = max(0.0, saturation)
            sharpness = max(0.0, sharpness)
            
            logger.debug(f"应用图像增强 - 亮度: {brightness}, 对比度: {contrast}, "
                        f"饱和度: {saturation}, 锐度: {sharpness}")
            
            # 转换到浮点数格式以避免溢出
            enhanced = frame.astype(np.float32) / 255.0
            
            # 亮度和对比度调整
            enhanced = (enhanced - 0.5) * contrast + 0.5 + brightness
            enhanced = np.clip(enhanced, 0, 1)
            
            # 饱和度调整
            if saturation != 1.0:
                try:
                    # 转换到HSV色彩空间
                    # 确保图像是3通道的RGB/BGR图像
                    if len(enhanced.shape) >= 3 and enhanced.shape[2] >= 3:
                        hsv = cv2.cvtColor((enhanced * 255).astype(np.uint8), cv2.COLOR_BGR2HSV)
                        hsv_float = hsv.astype(np.float32) / 255.0
                        
                        # 调整饱和度通道
                        hsv_float[:, :, 1] = np.clip(hsv_float[:, :, 1] * saturation, 0, 1)
                        
                        # 转换回BGR
                        enhanced = cv2.cvtColor((hsv_float * 255).astype(np.uint8), cv2.COLOR_HSV2BGR).astype(np.float32) / 255.0
                        logger.debug("饱和度调整完成")
                    else:
                        logger.warning("图像不是3通道格式，跳过饱和度调整")
                except Exception as e:
                    logger.error(f"饱和度调整失败: {str(e)}", exc_info=True)
            
            # 锐度调整
            if sharpness > 0:
                try:
                    # 使用高斯模糊计算锐化掩码
                    blurred = cv2.GaussianBlur(enhanced, (0, 0), 3)
                    sharpened = enhanced + sharpness * (enhanced - blurred)
                    enhanced = np.clip(sharpened, 0, 1)
                    logger.debug("锐度调整完成")
                except Exception as e:
                    logger.error(f"锐度调整失败: {str(e)}", exc_info=True)
            
            # 转换回8位格式
            result = (enhanced * 255).astype(np.uint8)
            logger.debug("图像增强处理完成")
            return result
        except Exception as e:
            logger.error(f"图像增强失败: {str(e)}", exc_info=True)
            # 返回原始帧以允许系统继续运行
            return frame
    
    def _apply_roi(self, frame: np.ndarray, roi_points: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """应用感兴趣区域
        
        Args:
            frame: 输入帧
            roi_points: ROI多边形点
            
        Returns:
            (处理后的帧, ROI掩码)
        """
        try:
            # 验证输入帧
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame, np.array([]) if frame is not None else np.array([])
                
            # 验证ROI点
            if roi_points is None or not isinstance(roi_points, np.ndarray):
                logger.error(f"无效的ROI点: {type(roi_points)}")
                return frame, np.zeros(frame.shape[:2], dtype=np.uint8)
                
            # 确保ROI点格式正确
            if roi_points.ndim != 2 or roi_points.shape[1] != 2:
                logger.error(f"ROI点格式无效: {roi_points.shape}，应为(N,2)格式")
                # 尝试重塑ROI点（如果可能）
                try:
                    roi_points = roi_points.reshape(-1, 2)
                    logger.warning(f"重塑ROI点为: {roi_points.shape}")
                except:
                    logger.error("无法重塑ROI点，返回原始帧")
                    return frame, np.zeros(frame.shape[:2], dtype=np.uint8)
            
            # 确保ROI点为整数类型
            if roi_points.dtype != np.int32 and roi_points.dtype != np.int64:
                roi_points = roi_points.astype(np.int32)
                logger.debug("将ROI点转换为整数类型")
            
            logger.debug(f"应用ROI，点数量: {len(roi_points)}")
            
            # 创建掩码
            mask = np.zeros_like(frame)
            
            try:
                # 填充ROI区域
                cv2.fillPoly(mask, [roi_points], (255, 255, 255))
                
                # 应用掩码
                masked_frame = cv2.bitwise_and(frame, mask)
                
                logger.debug("ROI应用成功")
                # 返回掩码的第一通道（灰度掩码）
                if len(mask.shape) >= 3:
                    return masked_frame, mask[:, :, 0]
                else:
                    return masked_frame, mask
            except Exception as e:
                logger.error(f"ROI填充或掩码应用失败: {str(e)}", exc_info=True)
                # 如果出错，返回原始帧和全黑掩码
                return frame, np.zeros(frame.shape[:2], dtype=np.uint8)
        except Exception as e:
            logger.error(f"应用ROI失败: {str(e)}", exc_info=True)
            # 返回原始帧和空掩码以允许系统继续运行
            return frame, np.zeros(frame.shape[:2], dtype=np.uint8) if frame is not None else np.array([])
    
    def postprocess(self, frame: np.ndarray, detections: List[Dict] = None,
                   tracking_results: List[Dict] = None, camera_info: Dict = None,
                   fps: float = 0.0) -> np.ndarray:
        """后处理视频帧
        
        Args:
            frame: 输入帧
            detections: 检测结果
            tracking_results: 跟踪结果
            camera_info: 摄像头信息
            fps: 当前帧率
            
        Returns:
            后处理后的帧
        """
        try:
            logger.debug("开始帧后处理")
            
            # 验证输入帧
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame
            
            # 验证检测结果格式
            if detections is not None and not isinstance(detections, list):
                logger.error(f"检测结果格式无效: {type(detections)}")
                detections = None
            
            # 验证跟踪结果格式
            if tracking_results is not None and not isinstance(tracking_results, list):
                logger.error(f"跟踪结果格式无效: {type(tracking_results)}")
                tracking_results = None
            
            # 验证摄像头信息格式
            if camera_info is not None and not isinstance(camera_info, dict):
                logger.error(f"摄像头信息格式无效: {type(camera_info)}")
                camera_info = None
            
            # 验证FPS
            if fps is not None and not isinstance(fps, (int, float)):
                logger.error(f"FPS格式无效: {type(fps)}")
                fps = 0.0
            
            # 复制帧以避免修改原始数据
            try:
                result_frame = frame.copy()
                logger.debug(f"成功复制输入帧，形状: {result_frame.shape}")
            except Exception as e:
                logger.error(f"无法复制输入帧: {str(e)}", exc_info=True)
                # 如果复制失败，直接使用原始帧
                result_frame = frame
            
            # 绘制检测结果
            try:
                if detections:
                    logger.debug(f"绘制 {len(detections)} 个检测结果")
                    result_frame = self._draw_detections(result_frame, detections)
            except Exception as e:
                logger.error(f"绘制检测结果失败: {str(e)}", exc_info=True)
            
            # 绘制跟踪结果
            try:
                if tracking_results:
                    logger.debug(f"绘制 {len(tracking_results)} 个跟踪结果")
                    result_frame = self._draw_tracking(result_frame, tracking_results)
            except Exception as e:
                logger.error(f"绘制跟踪结果失败: {str(e)}", exc_info=True)
            
            # 绘制显示信息
            try:
                if self.show_fps or self.show_timestamp or self.show_camera_info:
                    logger.debug("绘制显示信息")
                    result_frame = self._draw_info(
                        result_frame,
                        fps=fps if self.show_fps else None,
                        camera_info=camera_info if self.show_camera_info else None
                    )
            except Exception as e:
                logger.error(f"绘制显示信息失败: {str(e)}", exc_info=True)
            
            # 绘制ROI和检测区域
            try:
                if self.roi_points is not None:
                    logger.debug("绘制ROI")
                    result_frame = self._draw_roi(result_frame, self.roi_points)
            except Exception as e:
                logger.error(f"绘制ROI失败: {str(e)}", exc_info=True)
            
            try:
                for zone in self.detection_zones:
                    logger.debug(f"绘制检测区域: {zone.get('name', '未命名区域')}")
                    result_frame = self._draw_detection_zone(result_frame, zone)
            except Exception as e:
                logger.error(f"绘制检测区域失败: {str(e)}", exc_info=True)
            
            logger.debug("帧后处理完成")
            return result_frame
            
        except Exception as e:
            logger.error(f"帧后处理整体失败: {str(e)}", exc_info=True)
            # 返回原始帧以允许系统继续运行
            return frame
    
    def _draw_detections(self, frame: np.ndarray, detections: List[Dict]) -> np.ndarray:
        """绘制检测结果
        
        Args:
            frame: 输入帧
            detections: 检测结果列表
            
        Returns:
            绘制后的帧
        """
        try:
            # 验证输入参数
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame
            
            if detections is None or not isinstance(detections, list):
                logger.error(f"无效的检测结果: {type(detections)}")
                return frame
            
            logger.debug(f"绘制 {len(detections)} 个检测结果")
            
            # 类别颜色映射
            colors = {
                'person': (0, 0, 255),    # 红色
                'car': (0, 255, 0),      # 绿色
                'bicycle': (255, 165, 0), # 橙色
                'motorcycle': (255, 0, 255), # 紫色
                'truck': (0, 255, 255)    # 青色
            }
            
            # 绘制每个检测结果
            for i, det in enumerate(detections):
                try:
                    # 验证检测结果是否为字典类型
                    if not isinstance(det, dict):
                        logger.warning(f"检测 #{i} 不是字典类型: {type(det)}")
                        continue
                    
                    # 检查必要字段是否存在
                    required_fields = ['x1', 'y1', 'x2', 'y2', 'class_name', 'confidence']
                    if not all(field in det for field in required_fields):
                        logger.warning(f"检测 #{i} 缺少必要字段: {det}")
                        continue
                    
                    # 获取检测框坐标
                    try:
                        x1, y1 = int(det['x1']), int(det['y1'])
                        x2, y2 = int(det['x2']), int(det['y2'])
                    except (ValueError, TypeError) as e:
                        logger.warning(f"检测 #{i} 坐标转换失败: {str(e)}")
                        continue
                    
                    # 验证坐标有效性
                    if x1 >= x2 or y1 >= y2:
                        logger.warning(f"检测 #{i} 坐标无效: ({x1},{y1}) -> ({x2},{y2})")
                        continue
                    
                    # 验证坐标在图像范围内
                    h, w = frame.shape[:2]
                    if x1 < 0 or y1 < 0 or x2 > w or y2 > h:
                        logger.debug(f"检测 #{i} 坐标超出图像范围，裁剪到有效区域")
                        # 裁剪到图像范围内
                        x1 = max(0, x1)
                        y1 = max(0, y1)
                        x2 = min(w, x2)
                        y2 = min(h, y2)
                    
                    # 获取类别名称和置信度
                    class_name = det['class_name']
                    if not isinstance(class_name, str):
                        logger.warning(f"检测 #{i} 类别名称类型无效: {type(class_name)}")
                        class_name = "未知类别"
                    
                    confidence = det['confidence']
                    try:
                        confidence = float(confidence)
                    except (ValueError, TypeError):
                        logger.warning(f"检测 #{i} 置信度无效: {confidence}，使用默认值0.0")
                        confidence = 0.0
                    
                    # 获取颜色
                    color = colors.get(class_name, (128, 128, 128))
                    
                    # 验证颜色值
                    if not all(isinstance(c, int) and 0 <= c <= 255 for c in color):
                        logger.warning(f"检测 #{i} 颜色值无效: {color}，使用默认灰色")
                        color = (128, 128, 128)
                    
                    # 绘制边界框
                    try:
                        cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
                    except Exception as e:
                        logger.warning(f"检测 #{i} 绘制边界框失败: {str(e)}")
                        continue
                    
                    # 构建标签文本
                    label = f"{class_name} ({confidence:.2f})"
                    
                    # 绘制标签
                    try:
                        # 确保文本位置在图像内
                        text_y = max(20, y1 - 10)
                        cv2.putText(
                            frame,
                            label,
                            (x1, text_y),
                            cv2.FONT_HERSHEY_SIMPLEX,
                            0.5,
                            color,
                            2
                        )
                    except Exception as e:
                        logger.warning(f"检测 #{i} 绘制标签失败: {str(e)}")
                    
                except Exception as e:
                    logger.warning(f"处理单个检测 #{i} 失败: {str(e)}")
                    continue
            
            logger.debug("检测结果绘制完成")
            return frame
            
        except Exception as e:
            logger.error(f"绘制检测结果整体失败: {str(e)}", exc_info=True)
            # 返回原始帧以允许系统继续运行
            return frame
    
    def _draw_tracking(self, frame: np.ndarray, tracking_results: List[Dict]) -> np.ndarray:
        """绘制跟踪结果
        
        Args:
            frame: 输入帧
            tracking_results: 跟踪结果列表
            
        Returns:
            绘制后的帧
        """
        try:
            # 验证输入参数
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame
            
            if tracking_results is None or not isinstance(tracking_results, list):
                logger.error(f"无效的跟踪结果: {type(tracking_results)}")
                return frame
            
            logger.debug(f"绘制 {len(tracking_results)} 个跟踪结果")
            
            # 使用不同颜色区分不同的跟踪ID
            color_map = {}
            
            # 绘制每个跟踪结果
            for i, track in enumerate(tracking_results):
                try:
                    # 验证跟踪结果是否为字典类型
                    if not isinstance(track, dict):
                        logger.warning(f"跟踪 #{i} 不是字典类型: {type(track)}")
                        continue
                    
                    # 检查必要字段是否存在
                    required_fields = ['x1', 'y1', 'x2', 'y2', 'track_id', 'class_name']
                    if not all(field in track for field in required_fields):
                        logger.warning(f"跟踪 #{i} 缺少必要字段: {track}")
                        continue
                    
                    # 获取跟踪框坐标
                    try:
                        x1, y1 = int(track['x1']), int(track['y1'])
                        x2, y2 = int(track['x2']), int(track['y2'])
                    except (ValueError, TypeError) as e:
                        logger.warning(f"跟踪 #{i} 坐标转换失败: {str(e)}")
                        continue
                    
                    # 验证坐标有效性
                    if x1 >= x2 or y1 >= y2:
                        logger.warning(f"跟踪 #{i} 坐标无效: ({x1},{y1}) -> ({x2},{y2})")
                        continue
                    
                    # 验证坐标在图像范围内
                    h, w = frame.shape[:2]
                    if x1 < 0 or y1 < 0 or x2 > w or y2 > h:
                        logger.debug(f"跟踪 #{i} 坐标超出图像范围，裁剪到有效区域")
                        # 裁剪到图像范围内
                        x1 = max(0, x1)
                        y1 = max(0, y1)
                        x2 = min(w, x2)
                        y2 = min(h, y2)
                    
                    # 获取跟踪ID
                    track_id = track['track_id']
                    try:
                        track_id = int(track_id)
                    except (ValueError, TypeError):
                        logger.warning(f"跟踪 #{i} ID无效: {track_id}，使用默认值0")
                        track_id = 0
                    
                    # 获取类别名称
                    class_name = track['class_name']
                    if not isinstance(class_name, str):
                        logger.warning(f"跟踪 #{i} 类别名称类型无效: {type(class_name)}")
                        class_name = "未知类别"
                    
                    # 为跟踪ID生成唯一颜色
                    if track_id not in color_map:
                        color_map[track_id] = (
                            (track_id * 123) % 255,
                            (track_id * 321) % 255,
                            (track_id * 213) % 255
                        )
                    
                    color = color_map[track_id]
                    
                    # 验证颜色值
                    if not all(isinstance(c, int) and 0 <= c <= 255 for c in color):
                        logger.warning(f"跟踪 #{i} 颜色值无效: {color}，使用默认蓝色")
                        color = (255, 0, 0)
                    
                    # 绘制边界框
                    try:
                        cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
                    except Exception as e:
                        logger.warning(f"跟踪 #{i} 绘制边界框失败: {str(e)}")
                        continue
                    
                    # 绘制跟踪ID和类别
                    try:
                        label = f"ID: {track_id} ({class_name})"
                        # 确保文本位置在图像内
                        text_y = max(20, y1 - 10)
                        cv2.putText(
                            frame,
                            label,
                            (x1, text_y),
                            cv2.FONT_HERSHEY_SIMPLEX,
                            0.5,
                            color,
                            2
                        )
                    except Exception as e:
                        logger.warning(f"跟踪 #{i} 绘制标签失败: {str(e)}")
                    
                    # 如果有历史轨迹，绘制它
                    if 'history' in track and len(track['history']) > 1:
                        try:
                            history = track['history']
                            if not isinstance(history, list):
                                logger.warning(f"跟踪 #{i} 历史轨迹格式无效: {type(history)}")
                                continue
                                
                            valid_points = []
                            # 验证并处理轨迹点
                            for point in history:
                                if isinstance(point, (list, tuple)) and len(point) >= 2:
                                    try:
                                        px, py = float(point[0]), float(point[1])
                                        # 检查点是否在图像范围内
                                        if 0 <= px <= w and 0 <= py <= h:
                                            valid_points.append((int(px), int(py)))
                                    except (ValueError, TypeError):
                                        continue
                            
                            # 绘制轨迹线
                            for j in range(1, len(valid_points)):
                                prev_x, prev_y = valid_points[j-1]
                                curr_x, curr_y = valid_points[j]
                                cv2.line(frame, (prev_x, prev_y), (curr_x, curr_y), color, 2)
                            
                            logger.debug(f"跟踪 #{i} 成功绘制轨迹，有效点数: {len(valid_points)}")
                        except Exception as e:
                            logger.warning(f"跟踪 #{i} 绘制轨迹失败: {str(e)}")
                            continue
                except Exception as e:
                    logger.warning(f"处理单个跟踪 #{i} 失败: {str(e)}")
                    continue
            
            logger.debug("跟踪结果绘制完成")
            return frame
            
        except Exception as e:
            logger.error(f"绘制跟踪结果整体失败: {str(e)}", exc_info=True)
            # 返回原始帧以允许系统继续运行
            return frame
    
    def _draw_info(self, frame: np.ndarray, fps: float = None, 
                   camera_info: Dict = None) -> np.ndarray:
        """绘制显示信息
        
        Args:
            frame: 输入帧
            fps: FPS值
            camera_info: 摄像头信息
            
        Returns:
            绘制后的帧
        """
        try:
            # 验证输入参数
            if frame is None or not isinstance(frame, np.ndarray) or len(frame.shape) < 2:
                logger.error(f"无效的输入帧: {type(frame)}, 形状: {getattr(frame, 'shape', 'unknown')}")
                return frame
            
            logger.debug("开始绘制信息文本")
            info_lines = []
            
            # 获取图像尺寸
            h, w = frame.shape[:2]
            
            # 文本位置偏移量，确保在图像范围内
            base_x = min(10, w // 2)
            base_y = min(20, h - 10)
            line_height = 20
            
            # 添加FPS信息
            if fps is not None:
                try:
                    # 验证FPS类型
                    if not isinstance(fps, (int, float)):
                        logger.warning(f"FPS格式无效: {type(fps)}，尝试转换")
                        fps = float(fps)
                    info_lines.append(f"FPS: {fps:.1f}")
                    logger.debug(f"添加FPS信息: {fps:.1f}")
                except Exception as e:
                    logger.warning(f"处理FPS信息失败: {str(e)}")
            
            # 添加时间戳
            if self.show_timestamp:
                try:
                    current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    info_lines.append(f"Time: {current_time}")
                    logger.debug(f"添加时间戳: {current_time}")
                except Exception as e:
                    logger.warning(f"处理时间戳失败: {str(e)}")
            
            # 添加摄像头信息
            if camera_info:
                try:
                    # 验证摄像头信息类型
                    if not isinstance(camera_info, dict):
                        logger.warning(f"摄像头信息格式无效: {type(camera_info)}")
                    else:
                        camera_name = camera_info.get('name', 'Unknown')
                        resolution = camera_info.get('resolution', (0, 0))
                        info_lines.append(f"Camera: {camera_name}")
                        info_lines.append(f"Resolution: {resolution[0]}x{resolution[1]}")
                        logger.debug(f"添加摄像头信息: {camera_name}, 分辨率: {resolution[0]}x{resolution[1]}")
                except Exception as e:
                    logger.warning(f"处理摄像头信息失败: {str(e)}")
            
            # 添加场景信息
            if scene_info:
                try:
                    # 验证场景信息类型
                    if not isinstance(scene_info, dict):
                        logger.warning(f"场景信息格式无效: {type(scene_info)}")
                    else:
                        scene_type = scene_info.get('type', 'Unknown')
                        confidence = scene_info.get('confidence', 0.0)
                        info_lines.append(f"Scene: {scene_type}")
                        info_lines.append(f"Scene Confidence: {confidence:.2f}")
                        logger.debug(f"添加场景信息: {scene_type}, 置信度: {confidence:.2f}")
                except Exception as e:
                    logger.warning(f"处理场景信息失败: {str(e)}")
            
            # 绘制信息文本
            y_offset = base_y
            for i, line in enumerate(info_lines):
                try:
                    # 确保文本位置在图像内
                    current_y = y_offset + i * line_height
                    if current_y >= h - 10:
                        logger.warning("图像空间不足，跳过部分信息文本绘制")
                        break
                    
                    # 限制文本长度，避免超出图像宽度
                    max_text_width = int(w * 0.8)  # 最大文本宽度为图像宽度的80%
                    if len(line) > max_text_width:
                        line = line[:max_text_width] + "..."
                        
                    cv2.putText(
                        frame,
                        line,
                        (base_x, current_y),
                        cv2.FONT_HERSHEY_SIMPLEX,
                        0.5,
                        (0, 255, 0),
                        2
                    )
                except Exception as e:
                    logger.warning(f"绘制信息行 {i} 失败: {str(e)}")
                    continue
            
            logger.debug("信息文本绘制完成")
            return frame
            
        except Exception as e:
            logger.error(f"绘制信息文本整体失败: {str(e)}", exc_info=True)
            # 返回原始帧以允许系统继续运行
            return frame
    
    def _draw_roi(self, frame: np.ndarray, roi_points: np.ndarray) -> np.ndarray:
        """绘制ROI区域
        
        Args:
            frame: 输入帧
            roi_points: ROI多边形点
            
        Returns:
            绘制后的帧
        """
        # 绘制ROI多边形
        cv2.polylines(
            frame,
            [roi_points],
            isClosed=True,
            color=(0, 255, 255),
            thickness=2
        )
        
        # 绘制标签
        cv2.putText(
            frame,
            "ROI",
            tuple(roi_points[0]),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.5,
            (0, 255, 255),
            2
        )
        
        return frame
    
    def _draw_detection_zone(self, frame: np.ndarray, zone: Dict) -> np.ndarray:
        """绘制检测区域
        
        Args:
            frame: 输入帧
            zone: 区域信息字典
            
        Returns:
            绘制后的帧
        """
        points = np.array(zone['points'], dtype=np.int32)
        zone_name = zone.get('name', 'Zone')
        color = zone.get('color', (255, 0, 255))
        
        # 绘制区域多边形
        cv2.polylines(
            frame,
            [points],
            isClosed=True,
            color=color,
            thickness=2
        )
        
        # 绘制标签
        cv2.putText(
            frame,
            zone_name,
            tuple(points[0]),
            cv2.FONT_HERSHEY_SIMPLEX,
            0.5,
            color,
            2
        )
        
        return frame
    
    def set_roi(self, points: np.ndarray) -> None:
        """设置感兴趣区域
        
        Args:
            points: ROI多边形点的数组
        """
        self.roi_points = points
    
    def add_detection_zone(self, name: str, points: np.ndarray, color: Tuple = (255, 0, 255)) -> None:
        """添加检测区域
        
        Args:
            name: 区域名称
            points: 区域多边形点
            color: 区域颜色
        """
        self.detection_zones.append({
            'name': name,
            'points': points,
            'color': color
        })
    
    def clear_detection_zones(self) -> None:
        """清除所有检测区域"""
        self.detection_zones.clear()
    
    def set_enhancement_params(self, **kwargs) -> None:
        """设置图像增强参数
        
        Args:
            **kwargs: 增强参数
        """
        if 'brightness' in kwargs:
            self.brightness = kwargs['brightness']
        if 'contrast' in kwargs:
            self.contrast = kwargs['contrast']
        if 'saturation' in kwargs:
            self.saturation = kwargs['saturation']
        if 'sharpness' in kwargs:
            self.sharpness = kwargs['sharpness']
    
    def set_resize_params(self, width: int = 0, height: int = 0, 
                         maintain_aspect_ratio: bool = True, letterboxing: bool = False) -> None:
        """设置调整大小参数
        
        Args:
            width: 目标宽度
            height: 目标高度
            maintain_aspect_ratio: 是否保持宽高比
            letterboxing: 是否使用letterboxing填充
        """
        self.resize_width = width
        self.resize_height = height
        self.maintain_aspect_ratio = maintain_aspect_ratio
        self.letterboxing = letterboxing
    
    def set_display_params(self, show_fps: bool = True, show_timestamp: bool = True,
                          show_camera_info: bool = True) -> None:
        """设置显示参数
        
        Args:
            show_fps: 是否显示帧率
            show_timestamp: 是否显示时间戳
            show_camera_info: 是否显示摄像头信息
        """
        self.show_fps = show_fps
        self.show_timestamp = show_timestamp
        self.show_camera_info = show_camera_info

# 创建单例实例
_frame_processor_instance = None


def get_frame_processor() -> FrameProcessor:
    """获取帧处理器单例
    
    Returns:
        FrameProcessor: 帧处理器实例
    """
    global _frame_processor_instance
    
    if _frame_processor_instance is None:
        _frame_processor_instance = FrameProcessor()
    
    return _frame_processor_instance


# 更新类以支持线程安全
def update_frame_processor_settings(camera_id, settings):
    """更新帧处理器的摄像头设置
    
    Args:
        camera_id: 摄像头ID
        settings: 设置字典
    """
    try:
        processor = get_frame_processor()
        if hasattr(processor, 'camera_settings'):
            with processor.lock:
                processor.camera_settings[camera_id] = settings
            logger.info(f"已更新摄像头 {camera_id} 的帧处理器设置")
    except Exception as e:
        logger.error(f"更新帧处理器设置失败: {str(e)}")